#include "cborconverter_p.h"
#include "QtJsonSerializer/exception.h"

#include <QtCore/QSet>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
using namespace QtJsonSerializer;
using namespace QtJsonSerializer::TypeConverters;

bool CborConverter::canConvert(int metaTypeId) const
{
	static const QSet<int> metaTypes{
		QMetaType::QCborValue,
		QMetaType::QCborMap,
		QMetaType::QCborArray,
		QMetaType::QCborSimpleType,
		QMetaType::QJsonValue,
		QMetaType::QJsonObject,
		QMetaType::QJsonArray,
		QMetaType::QJsonDocument,
	};
	return metaTypes.contains(metaTypeId);
}

QList<QCborValue::Type> CborConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
{
	Q_UNUSED(tag)
		switch (metaTypeId) {
		case QMetaType::QCborValue: {
			QList<QCborValue::Type> types;
			const auto metaEnum = QMetaEnum::fromType<QCborValue::Type>();
			types.reserve(metaEnum.keyCount());
			for (auto i = 0; i < metaEnum.keyCount(); ++i) {
				if (const auto value = metaEnum.value(i); value != QCborValue::Invalid)
					types.append(static_cast<QCborValue::Type>(value));
			}
			return types;
		}
		case QMetaType::QJsonValue:
			return {
				QCborValue::Null,
				QCborValue::True,
				QCborValue::False,
				QCborValue::Double,
				QCborValue::String,
				QCborValue::Array,
				QCborValue::Map
			};
		case QMetaType::QCborSimpleType:
			return {
				QCborValue::SimpleType,
				QCborValue::True,
				QCborValue::False,
				QCborValue::Null,
				QCborValue::Undefined
			};
		case QMetaType::QCborMap:
		case QMetaType::QJsonObject:
			return { QCborValue::Map };
		case QMetaType::QCborArray:
		case QMetaType::QJsonArray:
			return { QCborValue::Array };
		case QMetaType::QJsonDocument:
			return { QCborValue::Map, QCborValue::Array, QCborValue::Null };
		default:
			Q_UNREACHABLE();
		}
}

QCborValue CborConverter::serialize(int propertyType, const QVariant& value) const
{
	switch (propertyType) {
	case QMetaType::QCborValue:
	case QMetaType::QCborSimpleType:
	case QMetaType::QCborMap:
	case QMetaType::QCborArray:
		return value.value<QCborValue>();
	case QMetaType::QJsonValue:
	case QMetaType::QJsonObject:
	case QMetaType::QJsonArray:
	case QMetaType::QJsonDocument:
		return QCborValue::fromJsonValue(value.toJsonValue());
	default:
		throw SerializationException{ "Unsupported type" };
	}
}

QVariant CborConverter::deserializeCbor(int propertyType, const QCborValue& value, QObject* parent) const
{
	Q_UNUSED(parent)
		switch (propertyType) {
		case QMetaType::QJsonValue:
			return QVariant::fromValue(value.toJsonValue());
		case QMetaType::QJsonObject:
			return QVariant::fromValue(value.toJsonValue().toObject());
		case QMetaType::QJsonArray:
			return QVariant::fromValue(value.toJsonValue().toArray());
		case QMetaType::QJsonDocument: {
			const auto jValue = value.toJsonValue();
			switch (jValue.type()) {
			case QJsonValue::Array:
				return QVariant::fromValue(QJsonDocument{ jValue.toArray() });
			case QJsonValue::Object:
				return QVariant::fromValue(QJsonDocument{ jValue.toObject() });
			default:
				return QVariant::fromValue(QJsonDocument{});
			}
		}
		case QMetaType::QCborValue:
		case QMetaType::QCborSimpleType:
		case QMetaType::QCborMap:
		case QMetaType::QCborArray:
			return QVariant::fromValue(value);
		default:
			throw DeserializationException{ "Unsupported type" };
		}
}
